package com.cimpm.networkDisk.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.cimpm.networkDisk.entity.NetworkDiskFile;
import com.cimpm.networkDisk.entity.RecycleBin;
import com.cimpm.networkDisk.entity.paramEntity.DownloadParam;
import com.cimpm.networkDisk.entity.responseEntity.FileInfo;
import com.cimpm.networkDisk.utils.NetworkDiskFileIOUtil;
import com.hby.common.callback.AutoProcessControllerPart;
import com.hby.common.callback.NoEntityProcessInterface;
import com.hby.common.callback.NoEntityProcessPart;
import com.hby.common.entities.ResMsg;
import com.hby.common.entities.aspect.parameter.NoEntityParamInfo;
import com.hby.common.entities.aspect.parameter.ParamConverter;
import com.hby.common.entities.aspect.parameter.ServiceInfo;
import com.hby.common.exceptions.AutoException;
import com.hby.common.exceptions.ExceptionEnum;
import com.hby.common.services.Impl.mian.AutoEntityServiceImpl;
import com.hby.common.upload.service.ChunkUploadServiceImpl;
import com.mysql.cj.util.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Service
public class NetworkDiskFileServiceImpl extends AutoEntityServiceImpl<NetworkDiskFile> {

    @Autowired
    private ChunkUploadServiceImpl chunkUploadService;
    @Autowired
    private RecycleBinServiceImpl recycleBinService;

    @Override
    public void customService(AutoProcessControllerPart autoProcessControllerPart, ServiceInfo serviceInfo) throws Throwable {
    }

    public Object folderContainer(NoEntityParamInfo param) throws Throwable {
        NoEntityProcessPart noEntityProcessPart = new NoEntityProcessPart();
        noEntityProcessPart.setGetPart(new NoEntityProcessInterface() {
            @Override
            public Object onCallBack(Integer userId, String userName, ParamConverter param) throws Throwable {
                String path = userId + param.getString("path");
                return NetworkDiskFileIOUtil.getFolderContains(path);
            }
        });
        return noEntityProcessPart.process(param, true);
    }

    public Object searchFile(ParamConverter param, Integer userId, String userName) throws Throwable {
        String keyWord = param.getString("keyWord");
        if (StringUtils.isNullOrEmpty(keyWord))
            return null;
        QueryWrapper<NetworkDiskFile> wrapper = new QueryWrapper<>();
        wrapper.like("name", keyWord).eq("user_id", userId).eq("is_deleted", 0);
        List<NetworkDiskFile> networkDiskFiles = this.mapper.selectList(wrapper);
        List<FileInfo> files = new ArrayList<>();
        for (NetworkDiskFile fileDb : networkDiskFiles) {
            FileInfo file = new FileInfo(fileDb.getId(), fileDb.getPath(), fileDb.getUserId(), fileDb.getName());
            files.add(file);
        }
        return files;
    }

    @Transactional(rollbackFor = Exception.class)
    public Object editFileName(ParamConverter param, Integer userId, String userName) throws Throwable {
        try {
            String path = userName + param.getString("path");
            String oldName = param.getString("name");
            String newName = param.getString("newName");
            String newNameFinal = NetworkDiskFileIOUtil.hasSameName(NetworkDiskFileIOUtil.getStorePath() + path, param.getString("newName"));
            QueryWrapper<NetworkDiskFile> wrapper = new QueryWrapper<>();
            ;
            wrapper.eq("user_id", userId).eq("path", param.getString("path"))
                    .eq("name", param.getString("name")).eq("is_deleted", 0);
            NetworkDiskFile networkDiskFile = this.mapper.selectOne(wrapper);
            if (networkDiskFile == null)
                throw new AutoException(ExceptionEnum.NOT_EXIST, "db file" + path + param.getString("name") + "not exist");
            networkDiskFile.setName(newNameFinal);
            int stat = this.mapper.updateById(networkDiskFile);
            NetworkDiskFileIOUtil.renameFile(path, oldName, newNameFinal);
            return ResMsg.succeseeResOneMes("修改成功", 1001);
        } catch (Exception e) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return e;
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public Object editFolderName(ParamConverter param, Integer userId, String userName) throws Throwable {
        try {
            String pathNoUser = param.getString("path");

            String pathMain = userName + param.getString("path");
            String folderName = param.getString("name");
            String newName = param.getString("newName");
            String newNameFinal = NetworkDiskFileIOUtil.hasSameNameFolder(NetworkDiskFileIOUtil.getStorePath() + pathMain, newName);
            QueryWrapper<NetworkDiskFile> wrapper = new QueryWrapper<>();
            ;
            String pathOld = pathNoUser + folderName + "\\";
            String pathNew = pathNoUser + newNameFinal + "\\";

            wrapper.likeRight("path", pathOld)
                    .eq("user_id", userId);
            List<NetworkDiskFile> networkDiskFiles = this.mapper.selectList(wrapper);
            for (NetworkDiskFile netFile : networkDiskFiles) {
                String pathDb = netFile.getPath();
                String resultPath = replaceFirstMatchString(pathDb, pathOld, pathNew);
                netFile.setPath(resultPath);
                int stat = this.mapper.updateById(netFile);
                if (stat != 1)
                    throw new AutoException(ExceptionEnum.JDBC, netFile.getPath() + ":" + netFile.getName() + "deal error");
            }
            NetworkDiskFileIOUtil.renameFolder(pathMain, folderName, newNameFinal, true);
            return ResMsg.succeseeResOneMes("修改成功", 1001);
        } catch (Exception e) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return e;
        }
    }

    //originText:原始内容；replacedText：要被替代的内容；replaceText:替换内容
    private String replaceFirstMatchString(String originText, String replacedText, String replaceText) {
        Pattern p = Pattern.compile(replacedText);
        Matcher matcher = p.matcher(originText);
        return matcher.replaceFirst(replaceText);
    }

    public Object computeUploadMd5(ParamConverter param, Integer userId, String userName) throws Throwable {
        String md5 = param.getString("md5");
        QueryWrapper<NetworkDiskFile> wrapper = new QueryWrapper<>();
        wrapper.like("md5", md5).eq("is_link", 0);
        NetworkDiskFile fileExist = this.mapper.selectOne(wrapper);

        String path = param.getString("path");
        String name = param.getString("name");

        String finalName = NetworkDiskFileIOUtil.hasSameName(NetworkDiskFileIOUtil.getStorePath() + userId + path, name);
        if (fileExist != null) {

            NetworkDiskFile linkFile = new NetworkDiskFile();
            linkFile.setName(finalName);
            linkFile.setUserId(userId);
            linkFile.setPath(path);
            linkFile.setIsLink(1);
            linkFile.setLinkId(fileExist.getLinkId());
            linkFile.setMd5(md5);
            linkFile.setIsDeleted(0);
            int stat = this.mapper.insert(linkFile);
            if (stat != 1)
                throw new AutoException(ExceptionEnum.JDBC, linkFile.getPath() + ":" + linkFile.getName() + "deal error");
            //生成文件
            NetworkDiskFileIOUtil.createFile(path, finalName);
            return ResMsg.succeseeResOneMes("已上传成功", 1030);
        }
        Map<String, Object> map = new HashMap<>();
        map.put("name", finalName);
        return ResMsg.succeseeRes("未上传过相同文件", map);
    }

    public Object createFolder(ParamConverter param, Integer userId, String userName) throws Throwable {
        String path = param.getString("path");
        String name = param.getString("name");
        String finalName = NetworkDiskFileIOUtil.hasSameNameFolder(NetworkDiskFileIOUtil.getStorePath() + userId + path, name);
        NetworkDiskFileIOUtil.createFolder(userId + path, finalName);
        return ResMsg.successRes(null);
    }

    public void download(DownloadParam param, HttpServletResponse response) throws Throwable {
        //查询链接
        QueryWrapper<NetworkDiskFile> wrapper = new QueryWrapper<>();
        wrapper.eq("user_id", param.getUserId()).eq("path", param.getPath())
                .eq("name", param.getName()).eq("is_deleted", 0);
        NetworkDiskFile fileDb = this.mapper.selectOne(wrapper);
        if (fileDb.getIsLink().equals(1))
            fileDb = this.getEntityFromId(fileDb.getLinkId());

        String path = fileDb.getUserId() + fileDb.getPath() + fileDb.getName();
        String fileLocate = NetworkDiskFileIOUtil.getStorePath() + path;
        File file = new File(fileLocate);
        String fileName = file.getName();

        // 实现文件下载
        byte[] buffer = new byte[1024];
        FileInputStream fis = null;
        BufferedInputStream bis = null;
        try {
            fis = new FileInputStream(file);
            bis = new BufferedInputStream(fis);
            // 获取字节流
            OutputStream os = response.getOutputStream();
            int i = bis.read(buffer);
            while (i != -1) {
                os.write(buffer, 0, i);
                i = bis.read(buffer);
            }
            System.out.println("Download successfully!");
        } catch (Exception e) {
            throw new Exception("Download failed");
        } finally {
            if (bis != null) {
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public Object preview(ParamConverter param, Integer userId, String userName) throws Throwable {
        String path = param.getString("path");
        String name = param.getString("name");
        String fileExtension = NetworkDiskFileIOUtil.getFileExtension(name);
        if (fileExtension.equals(".pdf") || fileExtension.equals(".PDF")) {
            QueryWrapper<NetworkDiskFile> wrapper = new QueryWrapper<>();
            wrapper.eq("name", name).eq("user_id", userId).eq("path", path);
            NetworkDiskFile networkDiskFile = this.mapper.selectOne(wrapper);
            if (networkDiskFile == null) throw new AutoException(ExceptionEnum.JDBC, "file db has exception");
            if (networkDiskFile.getIsLink().equals(1)) {
                NetworkDiskFile entityFromId = this.getEntityFromId(networkDiskFile.getLinkId());
                return NetworkDiskFileIOUtil.getUrl() + entityFromId.getId() + entityFromId.getPath() + entityFromId.getName();
            } else
                return NetworkDiskFileIOUtil.getUrl() + userId + networkDiskFile.getPath() + networkDiskFile.getName();
        } else return ResMsg.succeseeResOneMes("非pdf文件", 1032);
    }

    @Transactional(rollbackFor = Exception.class)
    public Object deleteFile(ParamConverter param, Integer userId, String userName) throws Throwable {
        try {
            QueryWrapper<NetworkDiskFile> wrapper = new QueryWrapper<>();
            wrapper.eq("name", param.getString("name")).eq("user_id", userId)
                    .eq("path", param.getString("path")).eq("is_deleted", 0);
            NetworkDiskFile networkDiskFile = this.mapper.selectOne(wrapper);
            if (networkDiskFile == null)
                throw new AutoException(ExceptionEnum.JDBC, "file:" + param.getString("name") + " has be deleted");
            networkDiskFile.setIsDeleted(1);
            int statA = this.mapper.updateById(networkDiskFile);
            if (statA != 1) throw new AutoException(ExceptionEnum.JDBC, "deleted option has exception");
            RecycleBin recycleBin = new RecycleBin(networkDiskFile);
            recycleBin.setNameInRecycle(recycleBin.getName() + new Date().getTime() + "deleted");
            int statB = recycleBinService.mapper.insert(recycleBin);
            if (statB != 1) throw new AutoException(ExceptionEnum.JDBC, "recycleBin option has exception");

            NetworkDiskFileIOUtil.deleteFile(networkDiskFile.getUserId() + networkDiskFile.getPath(), networkDiskFile.getName(), recycleBin.getNameInRecycle());


            return ResMsg.successRes(null);

        } catch (Exception e) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return e;
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public Object recoverFile(ParamConverter param, Integer userId, String userName) throws Throwable {
        try {
            RecycleBin recycleBin = recycleBinService.getEntityFromId(param.getInt("recycleBinId"));
            int statA = recycleBinService.mapper.deleteById(recycleBin.getId());
            if (statA != 1) throw new AutoException(ExceptionEnum.JDBC, "recycleBin option has exception");

            String nameFinal = NetworkDiskFileIOUtil.hasSameName(NetworkDiskFileIOUtil.getStorePath() + recycleBin.getUserId() + recycleBin.getPath(), recycleBin.getName());

            NetworkDiskFile networkDiskFile = this.mapper.selectById(recycleBin.getOriginId());
            networkDiskFile.setIsDeleted(0);
            networkDiskFile.setName(nameFinal);
            this.mapper.updateById(networkDiskFile);

            NetworkDiskFileIOUtil.renameFile(recycleBin.getUserId() + recycleBin.getPath(), recycleBin.getNameInRecycle(), nameFinal);
            return ResMsg.successRes(null);
        } catch (Exception e) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return e;
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public Object deleteFolder(ParamConverter param, Integer userId, String userName) throws Throwable {
        try {
            String path = param.getString("path");
            String name = param.getString("name");

            RecycleBin recycleBin = new RecycleBin();
            recycleBin.setUserId(userId);
            recycleBin.setPath(path);
            recycleBin.setName(name);
            recycleBin.setNameInRecycle(name + new Date().getTime() + ".delete");
            recycleBin.setOriginId(null);
            recycleBin.setIsFolder(1);
            int insert = recycleBinService.mapper.insert(recycleBin);
            if (insert != 1) throw new AutoException(ExceptionEnum.JDBC, "recycleBin option has exception");
            NetworkDiskFileIOUtil.deleteFolder(userId + path, name, recycleBin.getNameInRecycle());
            return ResMsg.successRes(null);
        } catch (Exception e) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return e;
        }
    }

    //有问题，应该无法合并
    @Transactional(rollbackFor = Exception.class)
    public Object recoverFolder(ParamConverter param, Integer userId, String userName) throws Throwable {
        try {
            RecycleBin recycleBin = recycleBinService.getEntityFromId(param.getInt("recycleBinId"));
            int statA = recycleBinService.mapper.deleteById(recycleBin.getId());
            if (statA != 1) throw new AutoException(ExceptionEnum.JDBC, "recycleBin option has exception");

            NetworkDiskFileIOUtil.renameFolder(userId + recycleBin.getPath(), recycleBin.getNameInRecycle(), recycleBin.getName(), true);

            return ResMsg.successRes(null);
        } catch (Exception e) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return e;
        }
    }

    public Object mvFile(ParamConverter param, Integer userId, String userName) throws Throwable {
        return null;
    }

    public Object mvFolder(ParamConverter param, Integer userId, String userName) throws Throwable {
        return null;
    }
}
